package com.gitee.huanminabc.jcommon.multithreading;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 简易可重入读写锁封装，基于 JDK ReentrantReadWriteLock。
 * <p>
 * - 支持同一线程重入读锁/写锁，支持写锁降级为读锁。
 * - 写锁互斥，读锁可并发。
 * - 线程安全，适合高并发场景下的分组隔离。
 * <br>
 * <b>典型用法：</b>
 * <pre>
 *   SimpleReadWriteLock lock = new SimpleReadWriteLock();
 *   // 写操作
 *   lock.writeLock();
 *   try {
 *       // 执行写操作
 *   } finally {
 *       lock.unlock();
 *   }
 *   // 读操作
 *   lock.readLock();
 *   try {
 *       // 执行读操作
 *   } finally {
 *       lock.unlock();
 *   }
 * </pre>
 * <b>注意事项：</b><br>
 * - 每次加锁后必须配对调用 unlock，否则会导致死锁。<br>
 * - 支持同一线程重入。<br>
 * - 支持写锁降级为读锁（先持有写锁再加读锁，释放顺序反过来）。<br>
 * - 不支持跨线程解锁。<br>
 * - 不支持读锁升级为写锁（与 JDK 行为一致）。
 */
public class SimpleReadWriteLock implements LockReadWriteBase {

    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock writeLock = lock.writeLock();
    private final Lock readLock = lock.readLock();

    // 每个线程独立记录锁状态：null=未锁定, "read"=读锁, "write"=写锁
    private final ThreadLocal<String> threadLockType = ThreadLocal.withInitial(() -> null);

    // 记录每个线程的重入次数（支持可重入锁）
    private final ThreadLocal<Integer> readHoldCount = ThreadLocal.withInitial(() -> 0);
    private final ThreadLocal<Integer> writeHoldCount = ThreadLocal.withInitial(() -> 0);

    @Override
    public void readLock() {
        readLock.lock();
        try {
            // 增加读锁重入次数
            int count = readHoldCount.get() + 1;
            readHoldCount.set(count);

            // 首次获取读锁时记录类型
            if (count == 1) {
                threadLockType.set("read");
            }
        } catch (Exception e) {
            // 发生异常时释放锁
            readLock.unlock();
            throw e;
        }
    }

    @Override
    public boolean tryReadLock() {
        boolean acquired = readLock.tryLock();
        if (acquired) {
            try {
                int count = readHoldCount.get() + 1;
                readHoldCount.set(count);
                if (count == 1) {
                    threadLockType.set("read");
                }
            } catch (Exception e) {
                readLock.unlock();
                throw e;
            }
        }
        return acquired;
    }

    @Override
    public boolean tryReadLock(long timeout, TimeUnit unit) throws InterruptedException {
        boolean acquired = readLock.tryLock(timeout, unit);
        if (acquired) {
            try {
                int count = readHoldCount.get() + 1;
                readHoldCount.set(count);
                if (count == 1) {
                    threadLockType.set("read");
                }
            } catch (Exception e) {
                readLock.unlock();
                throw e;
            }
        }
        return acquired;
    }

    @Override
    public void writeLock() {
        writeLock.lock();
        try {
            // 增加写锁重入次数
            int count = writeHoldCount.get() + 1;
            writeHoldCount.set(count);

            // 首次获取写锁时记录类型
            if (count == 1) {
                threadLockType.set("write");
            }
        } catch (Exception e) {
            // 发生异常时释放锁
            writeLock.unlock();
            throw e;
        }
    }

    @Override
    public boolean tryWriteLock() {
        boolean acquired = writeLock.tryLock();
        if (acquired) {
            try {
                int count = writeHoldCount.get() + 1;
                writeHoldCount.set(count);
                if (count == 1) {
                    threadLockType.set("write");
                }
            } catch (Exception e) {
                writeLock.unlock();
                throw e;
            }
        }
        return acquired;
    }

    @Override
    public boolean tryWriteLock(long timeout, TimeUnit unit) throws InterruptedException {
        boolean acquired = writeLock.tryLock(timeout, unit);
        if (acquired) {
            try {
                int count = writeHoldCount.get() + 1;
                writeHoldCount.set(count);
                if (count == 1) {
                    threadLockType.set("write");
                }
            } catch (Exception e) {
                writeLock.unlock();
                throw e;
            }
        }
        return acquired;
    }

    @Override
    public void unlock() {
        int readCount = readHoldCount.get();
        int writeCount = writeHoldCount.get();
        if (readCount <= 0 && writeCount <= 0) {
            throw new IllegalMonitorStateException("当前线程未持有锁");
        }

        if (readCount > 0) {
            // 优先释放读锁
            readCount--;
            readHoldCount.set(readCount);
            readLock.unlock();
        } else if (writeCount > 0) {
            // 释放写锁
            writeCount--;
            writeHoldCount.set(writeCount);
            writeLock.unlock();
        }

        // 如果都为0，清除状态
        if (readHoldCount.get() == 0 && writeHoldCount.get() == 0) {
            threadLockType.remove();
            readHoldCount.remove();
            writeHoldCount.remove();
        }
    }
}
